#include <vector>

#include <nemesis.Semaphore.h>
#include <nemesis.Thread.h>
#include <nemesis.Runnable.h>
#include <nemesis.Logger.h>
#include <nemesis.TraceWriter.h>
#include <nemesis.Mutex.h>
#include <nemesis.Guard.h>
#include <nemesis.TraceMethod.h>

using namespace std;
using namespace nemesis;

Semaphore g_semaphore (0);
std::vector <int> g_data;

pthread_mutex_t x_mutex;
NRMutex g_mutex (x_mutex);
//Mutex g_mutex;

class Producer : public Runnable {
public:
   Producer () : Runnable ("Producer") {;}

private:
   void run () throw (RuntimeException);
};

class Consumer : public Runnable {
public:
   Consumer () : Runnable ("Consumer") {;}

private:
   void run () throw (RuntimeException);
};

int main ()
{
   Producer producer;
   Consumer consumer1;
   Consumer consumer2;
   Consumer consumer3;

   Thread t1, t2, t3, t4;

   int errorCode;
   
   pthread_mutex_init (&x_mutex, NULL);

   Logger::setLevel (Logger::Debug);
   Logger::initialize ("semaphore", new TraceWriter ("file.trace", 2048000));

   t1.start (producer);
   t2.start (consumer1);
   t3.start (consumer2);
   t4.start (consumer3);

   Logger::debug ("Join", FILE_LOCATION);
   
   while (t1.isRunning () || t2.isRunning () || t3.isRunning () || t4.isRunning ());
}

void Producer::run () 
   throw (RuntimeException) 
{
   TraceMethod ttmm ("Producer", "run", FILE_LOCATION);

   string msg;
   int n = 0;

   for (int n = 0; n < 100; n ++) {
      if (true) {
         Guard guard (g_mutex);
         g_data.push_back (n);
         msg = functions::asText ("Produce numero: ", n);
         Logger::debug (msg, FILE_LOCATION);
      }

      g_semaphore.signal ();
   }
   
   g_semaphore.signal ();
   g_semaphore.signal ();
   g_semaphore.signal ();
}

void Consumer::run () 
   throw (RuntimeException) 
{
   TraceMethod ttmm ("Consumer", "run", FILE_LOCATION);
   vector <int>::iterator ii;

   while (true) {
      Logger::write (Logger::Debug, "Waiting ... ", FILE_LOCATION);
   
      g_semaphore.wait ();

      Guard guard (g_mutex);

      if ((ii = g_data.begin ()) == g_data.end ()) 
         break;

      string msg (functions::asText ("Consume numero: ", *ii));
      Logger::debug (msg, FILE_LOCATION);      
      g_data.erase (ii);
   }
}

